﻿#pragma once

#include <ctime>
#include <initializer_list>
#include <memory>
#include <string>

namespace kratos {
namespace time {

// Enumerations for week day
enum class WeekdayEnum : int8_t {
  None = -1,
  Sunday = 0, // SUNDAY
  Monday,     // MONDAY
  Tuesday,    // TUESDAY
  Wednesday,  // WEDNESDAY
  Thursday,   // THURSDAY
  Friday,     // FRIDAY
  Saturday,   // SATURDAY
};

/**
 * 本地日期接口
 */
class LocalDate {
public:
  /**
   * 析构.
   *
   */
  virtual ~LocalDate() {}
  /**
   * 年.
   *
   * \return 年
   */
  virtual int year() const = 0;
  /**
   * 月.
   *
   * \return 月
   */
  virtual int month() const = 0;
  /**
   * 日.
   *
   * \return 日
   */
  virtual int day() const = 0;
  /**
   * 日-周.
   *
   * \return 日-周 @see WeekdayEnum
   */
  virtual WeekdayEnum weekday() const = 0;
  /**
   * 小时，24小时制.
   *
   * \return 小时
   */
  virtual int hour() const = 0;
  /**
   * 分.
   *
   * \return 分
   */
  virtual int minute() const = 0;
  /**
   * 秒.
   *
   * \return 秒
   */
  virtual int second() const = 0;
  /**
   * 1970/1/1 00:00:00 到现在流逝的秒数.
   *
   * \return
   */
  virtual std::time_t elapsed_seconds() const = 0;
  /**
   * 1970/1/1 00:00:00 到现在流逝的天数.
   *
   * \return
   */
  virtual int elapsed_days() const = 0;
  /**
   * 字符串描述，格式为%d/%d/%d %d:%d:%d.
   *
   * \return 字符串描述
   */
  virtual std::string to_string() const = 0;
  /**
   * 当前日期本周内的下一天.
   *
   * \return 当前日期本周内的下一天
   */
  virtual WeekdayEnum next_weekday() const = 0;
  /**
   * 当前日期本周内的上一天.
   *
   * \return 当前日期本周内的上一天
   */
  virtual WeekdayEnum prev_weekday() const = 0;
  /**
   * 当前日期在一周内的第几天.
   *
   * \return 当前日期在一周内的第几天
   */
  virtual int days_at_week() const = 0;
  /**
   * 当前日期在一月内的第几天.
   *
   * \return 当前日期在一月内的第几天
   */
  virtual int days_at_month() const = 0;
  /**
   * 当前日期在一年内的第几天.
   *
   * \return 当前日期在一年内的第几天
   */
  virtual int days_at_year() const = 0;
  /**
   * 当前日期所在月有多少天.
   *
   * \return 当前日期所在月有多少天
   */
  virtual int days_of_month() const = 0;
  /**
   * 当前日期所在年有多少天.
   *
   * \return 当前日期所在年有多少天.
   */
  virtual int days_of_year() const = 0;
  /**
   * 是否是本周最后一天, 周日
   *
   * \return 是否是本周最后一天
   */
  virtual bool is_last_day_of_week() const = 0;
  /**
   * 是否是本月最后一天.
   *
   * \return
   */
  virtual bool is_last_day_of_month() const = 0;
  /**
   * 是否是本年最后一天.
   *
   * \return
   */
  virtual bool is_last_day_of_year() const = 0;
};

/**
 * 本地时间接口.
 */
class LocalTime {
public:
  /**
   * 析构.
   *
   */
  virtual ~LocalTime() {}
  /**
   * 获取本地时间戳，毫秒.
   *
   * \return 本地时间戳，毫秒
   */
  virtual std::time_t get_millionsecond() noexcept = 0;
  /**
   * 获取本地时间戳，秒.
   *
   * \return 本地时间戳，秒
   */
  virtual std::time_t get_second() noexcept = 0;
  /**
   * 获取与UTC时间的差，秒.
   *
   * \return 与UTC时间的差，秒
   */
  virtual std::time_t utc_diff_second() noexcept = 0;
  /**
   * 获取当前时间与时间戳t相差的天数，时间戳单位为秒.
   *
   * \param t 时间戳，秒
   * \return 天数差
   */
  virtual int diff_days(std::time_t t) noexcept = 0;
  /**
   * 获取时间戳t1与时间戳t2相差的天数，时间戳单位为秒.
   *
   * \param t1 时间戳，秒
   * \param t2 时间戳，秒
   * \return 天数差
   */
  virtual int diff_days(std::time_t t1, std::time_t t2) noexcept = 0;
  /**
   * 获取当前日期的.
   *
   * \return 当前日期
   */
  virtual std::unique_ptr<LocalDate> get_date() noexcept = 0;
  /**
   * 根据日期字符串获取日期.
   *
   * \param fmt 日期字符串
   * \return 日期
   */
  virtual std::unique_ptr<LocalDate> from(const std::string &fmt) noexcept = 0;
  /**
   * 根据时间戳(秒)获取日期.
   *
   * \param t 时间戳(秒)
   * \return 日期
   */
  virtual std::unique_ptr<LocalDate> from(std::time_t t) noexcept = 0;
  /**
   * 检测两个时间戳是否是同一天.
   *
   * \param t1 时间戳(秒)
   * \param t2 时间戳(秒)
   * \return true或false
   */
  virtual bool in_same_day(std::time_t t1, std::time_t t2) noexcept = 0;
  /**
   * 检测两个时间戳是否是同一周.
   *
   * \param t1 时间戳(秒)
   * \param t2 时间戳(秒)
   * \return true或false
   */
  virtual bool in_same_week(std::time_t t1, std::time_t t2) noexcept = 0;
  /**
   * 检测两个时间戳是否是同一月.
   *
   * \param t1 时间戳(秒)
   * \param t2 时间戳(秒)
   * \return true或false
   */
  virtual bool in_same_month(std::time_t t1, std::time_t t2) noexcept = 0;
};

} // namespace time
} // namespace kratos
